[[expressions-operator-safe-navigation]]
= Safe Navigation Operator

The safe navigation operator (`?.`) is used to avoid a `NullPointerException` and comes
from the https://www.groovy-lang.org/operators.html#_safe_navigation_operator[Groovy]
language. Typically, when you have a reference to an object, you might need to verify
that it is not `null` before accessing methods or properties of the object. To avoid
this, the safe navigation operator returns `null` for the particular null-safe operation
instead of throwing an exception.

[WARNING]
====
When the safe navigation operator evaluates to `null` for a particular null-safe
operation within a compound expression, the remainder of the compound expression will
still be evaluated.

See <<expressions-operator-safe-navigation-compound-expressions>> for details.
====

[[expressions-operator-safe-navigation-property-access]]
== Safe Property and Method Access

The following example shows how to use the safe navigation operator for property access
(`?.`).

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
	ExpressionParser parser = new SpelExpressionParser();
	EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

	Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
	tesla.setPlaceOfBirth(new PlaceOfBirth("Smiljan"));

	// evaluates to "Smiljan"
	String city = parser.parseExpression("placeOfBirth?.city") // <1>
			.getValue(context, tesla, String.class);

	tesla.setPlaceOfBirth(null);

	// evaluates to null - does not throw NullPointerException
	city = parser.parseExpression("placeOfBirth?.city") // <2>
			.getValue(context, tesla, String.class);	
----
<1> Use safe navigation operator on non-null `placeOfBirth` property
<2> Use safe navigation operator on null `placeOfBirth` property

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
	val parser = SpelExpressionParser()
	val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()

	val tesla = Inventor("Nikola Tesla", "Serbian")
	tesla.setPlaceOfBirth(PlaceOfBirth("Smiljan"))

	// evaluates to "Smiljan"
	var city = parser.parseExpression("placeOfBirth?.city") // <1>
			.getValue(context, tesla, String::class.java)

	tesla.setPlaceOfBirth(null)

	// evaluates to null - does not throw NullPointerException
	city = parser.parseExpression("placeOfBirth?.city") // <2>
			.getValue(context, tesla, String::class.java)
----
<1> Use safe navigation operator on non-null `placeOfBirth` property
<2> Use safe navigation operator on null `placeOfBirth` property
======

[NOTE]
====
The safe navigation operator also applies to method invocations on an object.

For example, the expression `#calculator?.max(4, 2)` evaluates to `null` if the
`#calculator` variable has not been configured in the context. Otherwise, the
`max(int, int)` method will be invoked on the `#calculator`.
====

[[expressions-operator-safe-navigation-indexing]]
== Safe Index Access

Since Spring Framework 6.2, the Spring Expression Language supports safe navigation for
indexing into the following types of structures.

* xref:core/expressions/language-ref/properties-arrays.adoc#expressions-indexing-arrays-and-collections[arrays and collections]
* xref:core/expressions/language-ref/properties-arrays.adoc#expressions-indexing-strings[strings]
* xref:core/expressions/language-ref/properties-arrays.adoc#expressions-indexing-maps[maps]
* xref:core/expressions/language-ref/properties-arrays.adoc#expressions-indexing-objects[objects]
* xref:core/expressions/language-ref/properties-arrays.adoc#expressions-indexing-custom[custom]

The following example shows how to use the safe navigation operator for indexing into
a list (`?.[]`).

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
	ExpressionParser parser = new SpelExpressionParser();
	IEEE society = new IEEE();
	EvaluationContext context = new StandardEvaluationContext(society);

	// evaluates to Inventor("Nikola Tesla")
	Inventor inventor = parser.parseExpression("members?.[0]") // <1>
			.getValue(context, Inventor.class);

	society.members = null;

	// evaluates to null - does not throw an exception
	inventor = parser.parseExpression("members?.[0]") // <2>
			.getValue(context, Inventor.class);
----
<1> Use null-safe index operator on a non-null `members` list
<2> Use null-safe index operator on a null `members` list

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
	val parser = SpelExpressionParser()
	val society = IEEE()
	val context = StandardEvaluationContext(society)

	// evaluates to Inventor("Nikola Tesla")
	var inventor = parser.parseExpression("members?.[0]") // <1>
			.getValue(context, Inventor::class.java)

	society.members = null

	// evaluates to null - does not throw an exception
	inventor = parser.parseExpression("members?.[0]") // <2>
			.getValue(context, Inventor::class.java)
----
<1> Use null-safe index operator on a non-null `members` list
<2> Use null-safe index operator on a null `members` list
======

[[expressions-operator-safe-navigation-selection-and-projection]]
== Safe Collection Selection and Projection

The Spring Expression Language supports safe navigation for
xref:core/expressions/language-ref/collection-selection.adoc[collection selection] and
xref:core/expressions/language-ref/collection-projection.adoc[collection projection] via
the following operators.

* null-safe selection: `?.?`
* null-safe select first: `?.^`
* null-safe select last: `?.$`
* null-safe projection: `?.!`

The following example shows how to use the safe navigation operator for collection
selection (`?.?`).

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
	ExpressionParser parser = new SpelExpressionParser();
	IEEE society = new IEEE();
	StandardEvaluationContext context = new StandardEvaluationContext(society);
	String expression = "members?.?[nationality == 'Serbian']"; // <1>

	// evaluates to [Inventor("Nikola Tesla")]
	List<Inventor> list = (List<Inventor>) parser.parseExpression(expression)
			.getValue(context);

	society.members = null;

	// evaluates to null - does not throw a NullPointerException
	list = (List<Inventor>) parser.parseExpression(expression)
			.getValue(context);
----
<1> Use null-safe selection operator on potentially null `members` list

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
	val parser = SpelExpressionParser()
	val society = IEEE()
	val context = StandardEvaluationContext(society)
	val expression = "members?.?[nationality == 'Serbian']" // <1>

	// evaluates to [Inventor("Nikola Tesla")]
	var list = parser.parseExpression(expression)
			.getValue(context) as List<Inventor>

	society.members = null

	// evaluates to null - does not throw a NullPointerException
	list = parser.parseExpression(expression)
			.getValue(context) as List<Inventor>
----
<1> Use null-safe selection operator on potentially null `members` list
======

The following example shows how to use the "null-safe select first" operator for
collections (`?.^`).

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
	ExpressionParser parser = new SpelExpressionParser();
	IEEE society = new IEEE();
	StandardEvaluationContext context = new StandardEvaluationContext(society);
	String expression =
		"members?.^[nationality == 'Serbian' || nationality == 'Idvor']"; // <1>

	// evaluates to Inventor("Nikola Tesla")
	Inventor inventor = parser.parseExpression(expression)
			.getValue(context, Inventor.class);

	society.members = null;

	// evaluates to null - does not throw a NullPointerException
	inventor = parser.parseExpression(expression)
			.getValue(context, Inventor.class);
----
<1> Use "null-safe select first" operator on potentially null `members` list

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
	val parser = SpelExpressionParser()
	val society = IEEE()
	val context = StandardEvaluationContext(society)
	val expression =
		"members?.^[nationality == 'Serbian' || nationality == 'Idvor']" // <1>

	// evaluates to Inventor("Nikola Tesla")
	var inventor = parser.parseExpression(expression)
			.getValue(context, Inventor::class.java)

	society.members = null

	// evaluates to null - does not throw a NullPointerException
	inventor = parser.parseExpression(expression)
			.getValue(context, Inventor::class.java)
----
<1> Use "null-safe select first" operator on potentially null `members` list
======


The following example shows how to use the "null-safe select last" operator for
collections (`?.$`).

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
	ExpressionParser parser = new SpelExpressionParser();
	IEEE society = new IEEE();
	StandardEvaluationContext context = new StandardEvaluationContext(society);
	String expression =
		"members?.$[nationality == 'Serbian' || nationality == 'Idvor']"; // <1>

	// evaluates to Inventor("Pupin")
	Inventor inventor = parser.parseExpression(expression)
			.getValue(context, Inventor.class);

	society.members = null;

	// evaluates to null - does not throw a NullPointerException
	inventor = parser.parseExpression(expression)
			.getValue(context, Inventor.class);
----
<1> Use "null-safe select last" operator on potentially null `members` list

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
	val parser = SpelExpressionParser()
	val society = IEEE()
	val context = StandardEvaluationContext(society)
	val expression =
		"members?.$[nationality == 'Serbian' || nationality == 'Idvor']" // <1>

	// evaluates to Inventor("Pupin")
	var inventor = parser.parseExpression(expression)
			.getValue(context, Inventor::class.java)

	society.members = null

	// evaluates to null - does not throw a NullPointerException
	inventor = parser.parseExpression(expression)
			.getValue(context, Inventor::class.java)
----
<1> Use "null-safe select last" operator on potentially null `members` list
======

The following example shows how to use the safe navigation operator for collection
projection (`?.!`).

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
	ExpressionParser parser = new SpelExpressionParser();
	IEEE society = new IEEE();
	StandardEvaluationContext context = new StandardEvaluationContext(society);

	// evaluates to ["Smiljan", "Idvor"]
	List placesOfBirth = parser.parseExpression("members?.![placeOfBirth.city]") // <1>
			.getValue(context, List.class);

	society.members = null;

	// evaluates to null - does not throw a NullPointerException
	placesOfBirth = parser.parseExpression("members?.![placeOfBirth.city]") // <2>
			.getValue(context, List.class);
----
<1> Use null-safe projection operator on non-null `members` list
<2> Use null-safe projection operator on null `members` list

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
	val parser = SpelExpressionParser()
	val society = IEEE()
	val context = StandardEvaluationContext(society)

	// evaluates to ["Smiljan", "Idvor"]
	var placesOfBirth = parser.parseExpression("members?.![placeOfBirth.city]") // <1>
			.getValue(context, List::class.java)

	society.members = null

	// evaluates to null - does not throw a NullPointerException
	placesOfBirth = parser.parseExpression("members?.![placeOfBirth.city]") // <2>
			.getValue(context, List::class.java)
----
<1> Use null-safe projection operator on non-null `members` list
<2> Use null-safe projection operator on null `members` list
======


[[expressions-operator-safe-navigation-compound-expressions]]
== Null-safe Operations in Compound Expressions

As mentioned at the beginning of this section, when the safe navigation operator
evaluates to `null` for a particular null-safe operation within a compound expression,
the remainder of the compound expression will still be evaluated. This means that the
safe navigation operator must be applied throughout a compound expression in order to
avoid any unwanted `NullPointerException`.

Given the expression `#person?.address.city`, if `#person` is `null` the safe navigation
operator (`?.`) ensures that no exception will be thrown when attempting to access the
`address` property of `#person`. However, since `#person?.address` evaluates to `null`, a
`NullPointerException` will be thrown when attempting to access the `city` property of
`null`. To address that, you can apply null-safe navigation throughout the compound
expression as in `#person?.address?.city`. That expression will safely evaluate to `null`
if either `#person` or `#person?.address` evaluates to `null`.

The following example demonstrates how to use the "null-safe select first" operator
(`?.^`) on a collection combined with null-safe property access (`?.`) within a compound
expression. If `members` is `null`, the result of the "null-safe select first" operator
(`members?.^[nationality == 'Serbian']`) evaluates to `null`, and the additional use of
the safe navigation operator (`?.name`) ensures that the entire compound expression
evaluates to `null` instead of throwing an exception.

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
	ExpressionParser parser = new SpelExpressionParser();
	IEEE society = new IEEE();
	StandardEvaluationContext context = new StandardEvaluationContext(society);
	String expression = "members?.^[nationality == 'Serbian']?.name"; // <1>

	// evaluates to "Nikola Tesla"
	String name = parser.parseExpression(expression)
			.getValue(context, String.class);

	society.members = null;

	// evaluates to null - does not throw a NullPointerException
	name = parser.parseExpression(expression)
			.getValue(context, String.class);
----
<1> Use "null-safe select first" and null-safe property access operators within compound expression.

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
	val parser = SpelExpressionParser()
	val society = IEEE()
	val context = StandardEvaluationContext(society)
	val expression = "members?.^[nationality == 'Serbian']?.name" // <1>

	// evaluates to "Nikola Tesla"
	String name = parser.parseExpression(expression)
			.getValue(context, String::class.java)

	society.members = null

	// evaluates to null - does not throw a NullPointerException
	name = parser.parseExpression(expression)
			.getValue(context, String::class.java)
----
<1> Use "null-safe select first" and null-safe property access operators within compound expression.
======
